package com.px.lovepath.utils.network;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolException;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultRedirectHandler;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

import android.content.Context;
import android.os.Looper;
import android.text.TextUtils;

public class HttpClientManager implements NetworkListener {
	private static HttpClientManager mSingleton = null;
	private final NetworkMonitor mNetworkMonitor;
	private HttpHost mHttpProxy = null;
	private final DefaultHttpClient mDefaultHttpClient;
	private final HashMap<String, DefaultHttpClient> mHttpClientMap = new HashMap<String, DefaultHttpClient>();
	
	// ### 内嵌类 ###
	// HTTP客户端相关设置
	private static class HttpClientSettings {
		// 用户代理(伪装代理参见http://www.zytrax.com/tech/web/browser_ids.htm)
		public static final String DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; Creative AutoUpdate v1.40.02)";
		// 连接超时
		public static final int CONNECTION_TIMEOUT = 30 * 1000;
		// 通讯超时
		public static final int SOCKET_TIMEOUT = 30 * 1000;
		// 最大并发连接数
		public static final int MAX_TOTAL_CONNECTION_COUNT = 10;
		// 最大每路由并发连接数
		public static final int MAX_ROUTE_CONNECTION_COUNT = 10;
	}
	// HTTP重定向处理器
	private static class HttpRedirectHandler extends DefaultRedirectHandler {
		@Override
		public URI getLocationURI(HttpResponse response, HttpContext context) throws ProtocolException {
			HttpHost host = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
			assert host != null;

			URI resolvedUri = null;
			try {
				URI locationUri = super.getLocationURI(response, context);
				URI baseUri = new URI(host.toURI());
				resolvedUri = URIUtils.resolve(baseUri, locationUri);
			} catch (Exception e) {
				e.printStackTrace();
			}

			if (resolvedUri == null || HttpHelper.hasInvalidUriChar(resolvedUri.toString())) {
				
				try {
					Header locationHeader = response.getFirstHeader(HttpHeaders.LOCATION);
					String locationValue = locationHeader.getValue();
					if (locationValue == null)
						return null;
					
					String encodedUri = HttpHelper.validateResponseUri(response, locationValue); 
					URI locationUri = new URI(encodedUri); 
					URI baseUri = new URI(host.toURI());
					resolvedUri = URIUtils.resolve(baseUri, locationUri);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			if (resolvedUri != null) {
				context.setAttribute("_http.redirect_location", resolvedUri.toString());

				assert response.getStatusLine() != null;
				if (response.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY)
					context.setAttribute("_http.permanent_redirect_location", resolvedUri.toString());
			}
			return resolvedUri;
		}
		@Override
		public boolean isRedirectRequested(HttpResponse response, HttpContext context) {
			boolean requestsRedirect = super.isRedirectRequested(response, context);
			if (requestsRedirect) {
				
			}
			return requestsRedirect;
		}
	}

	// ### 构造函数 ###
	protected HttpClientManager(Context context, NetworkMonitor networkMonitor) {
		assert networkMonitor != null;
		mNetworkMonitor = networkMonitor;
		mNetworkMonitor.addNetworkListener(this);
		mHttpProxy = mNetworkMonitor.getHttpProxy();
		mDefaultHttpClient = doNewHttpClient(HttpClientSettings.DEFAULT_USER_AGENT);
	}
	
	// ### NetworkListener方法实现 ###
	@Override
	public synchronized void onNetworkConnected(NetworkMonitor networkMonitor) {
		mHttpProxy = networkMonitor.getHttpProxy();
		
		setHttpClientProxy(mDefaultHttpClient, mHttpProxy);
		for (Map.Entry<String, DefaultHttpClient> entry : mHttpClientMap.entrySet()) {
			DefaultHttpClient httpClient = entry.getValue();
			assert httpClient != null;

			setHttpClientProxy(httpClient, mHttpProxy);
		}
	}
	@Override
	public synchronized void onNetworkDisconnected(NetworkMonitor networkMonitor) {
		
	}
	
	// ### 属性 ###
	public synchronized DefaultHttpClient getDefaultHttpClient() {
		assert mDefaultHttpClient != null;
		
		return mDefaultHttpClient;
	}
	
	// ### 方法 ###
	public synchronized DefaultHttpClient newHttpClient(String clientName) {
		return newHttpClient(clientName, HttpClientSettings.DEFAULT_USER_AGENT);
	}
	public synchronized DefaultHttpClient newHttpClient(String clientName, String userAgent) {
		if (mHttpClientMap.containsKey(clientName))
			return null;
		
		if (TextUtils.isEmpty(clientName))
			return null;
		
		DefaultHttpClient newHttpClient = doNewHttpClient(userAgent);
		mHttpClientMap.put(clientName, newHttpClient);
		return newHttpClient;
	}
	public synchronized DefaultHttpClient getHttpClient(String clientName) {
		if (TextUtils.isEmpty(clientName)) {
			return getDefaultHttpClient();
		}
		return mHttpClientMap.get(clientName);
	}
	public synchronized static void startup(Context context, NetworkMonitor networkMonitor) {
		assert Thread.currentThread() == Looper.getMainLooper().getThread();
		assert mSingleton == null;
		
		mSingleton = new HttpClientManager(context, networkMonitor);
	}
	public synchronized static HttpClientManager get() {
		assert mSingleton != null;
		return mSingleton;
	}
	
	// ### 实现函数 ###
	private DefaultHttpClient doNewHttpClient(String userAgent) {
		BasicHttpParams httpParams = new BasicHttpParams();

		// 设置HTTP协议相关参数
		if (userAgent != null)
			HttpProtocolParams.setUserAgent(httpParams, userAgent);

		// 设置HTTP连接相关参数
		HttpConnectionParams.setStaleCheckingEnabled(httpParams, false); // 不检测陈旧连接, 提高效率.
		HttpConnectionParams.setConnectionTimeout(httpParams, HttpClientSettings.CONNECTION_TIMEOUT); // 设置连接超时
		HttpConnectionParams.setSoTimeout(httpParams, HttpClientSettings.SOCKET_TIMEOUT); // 设置通讯超时
		HttpConnectionParams.setSocketBufferSize(httpParams, 8192); // 设置通讯缓冲区大小

		// 设置HTTP客户端相关参数
		HttpClientParams.setRedirecting(httpParams, true); // 设置允许自动处理重定向
		
		// 设置HTTP连接管理器相关参数
		ConnManagerParams.setTimeout(httpParams, HttpClientSettings.CONNECTION_TIMEOUT * 3); // 设置创建连接超时 
		ConnManagerParams.setMaxTotalConnections(httpParams, HttpClientSettings.MAX_TOTAL_CONNECTION_COUNT); // 设置最大并发连接数
		ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(HttpClientSettings.MAX_ROUTE_CONNECTION_COUNT)); // 设置最大每路由并发连接数
		
		// 指定传输层协议
		SchemeRegistry schemeRegistry = new SchemeRegistry();
		schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));

		// 创建HTTP连接管理器
		ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
		
		// 创建HTTP客户端
		DefaultHttpClient newHttpClient = new DefaultHttpClient(connectionManager, httpParams);
		newHttpClient.setRedirectHandler(new HttpRedirectHandler());
		assert newHttpClient != null;
		
		// 设置代理
		setHttpClientProxy(newHttpClient, mHttpProxy);
		
		return newHttpClient;
	}
	private void setHttpClientProxy(DefaultHttpClient httpClient, HttpHost httpProxy) {
		if (httpProxy != null)
			httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, httpProxy);
		else
			httpClient.getParams().removeParameter(ConnRoutePNames.DEFAULT_PROXY);
	}
}
